home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Pascal / Snippets / PNL Libraries / MyTraceroute.p < prev    next >
Text File  |  1996-11-04  |  10KB  |  402 lines

  1. unit MyTraceroute;
  2.  
  3. { based on Quinn's DTS sample code }
  4.  
  5. interface
  6.  
  7.     uses
  8.         Types, OpenTransport, OpenTptInternet;
  9.         
  10.     type
  11.         ICMPRecordedInformation = record
  12.             sent_time: UnsignedWide;
  13.             arrival_time: UnsignedWide;
  14.             remote_ip: InetHost;
  15.             typecode: integer;
  16.             ttl: integer;
  17.             udp_remote_port: InetPort;
  18.         end;
  19.     
  20.     const
  21.         max_icmp_results = 100;
  22.         null_traceroute_index = -1;
  23.  
  24.     var
  25.         icmp_results: array[1..max_icmp_results] of ICMPRecordedInformation;
  26.         traceroutes_in_progress: longint;
  27.  
  28.     procedure StartupMyTraceroute;
  29.     
  30.     function StartTraceroute: OSStatus; { Start & Stop may be nested and must be paired }
  31.     procedure StopTraceroute;
  32.     
  33.     function FindFreeResultIndex: integer;
  34.     procedure FreeResultIndex( var index: integer );
  35.  
  36.     function SendTraceroutePacket( dest: InetHost; index: integer; ttl: longint ): OSStatus;
  37.  
  38. implementation
  39.  
  40.     uses
  41.         Events, Timer,
  42.         MyCStrings, MyLookFreeOT, MyTransport, MyStartup, MyMemory;
  43.  
  44.     const
  45.         min_remote_port = 33434;
  46.         max_remote_port = 34433;
  47.         
  48.     var
  49.         udp_ep, rawip_ep: EndpointRef;
  50.         udp_local_port: InetPort;
  51.         next_udp_remote_port: InetPort;
  52.     
  53.     procedure InitResults;
  54.         var
  55.             i: integer;
  56.     begin
  57.         for i := 1 to max_icmp_results do begin
  58.             icmp_results[i].udp_remote_port := 0;
  59.         end;
  60.     end;
  61.     
  62.     function FindResultIndex( port: InetPort ): integer;
  63.         var
  64.             i: integer;
  65.             result: integer;
  66.     begin
  67.         result := null_traceroute_index;
  68.         if (min_remote_port <= port) & (port <= max_remote_port) then begin
  69.             for i := 1 to max_icmp_results do begin
  70.                 if icmp_results[i].udp_remote_port = port then begin
  71.                     result := i;
  72.                     leave;
  73.                 end;
  74.             end;
  75.         end;
  76.         FindResultIndex := result;
  77.     end;
  78.     
  79.     procedure FreeResultIndex( var index: integer );
  80.     begin
  81.         if index <> null_traceroute_index then begin
  82.             icmp_results[index].udp_remote_port := 0;
  83.             index := null_traceroute_index;
  84.         end;
  85.     end;
  86.     
  87.     function FindFreeResultIndex: integer;
  88.         var
  89.             i: integer;
  90.             result: integer;
  91.     begin
  92.         result := null_traceroute_index;
  93.         for i := 1 to max_icmp_results do begin
  94.             if icmp_results[i].udp_remote_port = 0 then begin
  95.                 icmp_results[i].remote_ip := 0;
  96.                 icmp_results[i].udp_remote_port := next_udp_remote_port;
  97.                 next_udp_remote_port := next_udp_remote_port + 1;
  98.                 if next_udp_remote_port > max_remote_port then begin
  99.                     next_udp_remote_port := min_remote_port;
  100.                 end;
  101.                 result := i;
  102.                 leave;
  103.             end;
  104.         end;
  105.         FindFreeResultIndex := result;
  106.     end;
  107.     
  108.     procedure RawIPEventHandler ( ep: EndpointRef; event: OTEventCode; result: OTResult; cookie: univ Ptr);
  109.         type
  110.             UDPReplyData = packed record
  111.                 local_port: InetPort;
  112.                 remote_port: InetPort;
  113.                 len: integer;
  114.                 checksum: integer;
  115.             end;
  116.             UDPReplyDataPtr = ^UDPReplyData;
  117.         var
  118.             err: OSStatus;
  119.             packet:packed array[0..1023] of Byte;
  120.             udata: TUnitData;
  121.             src_addr: InetAddress;
  122.             header1_size, header2_size: integer;
  123.             udp: UDPReplyDataPtr;
  124.             index: integer;
  125.             flags: OTFlags;
  126.     begin
  127. {$unused(cookie, result)}
  128.         case event of
  129.             T_DATA, T_GODATA: begin
  130.                 while true do begin
  131.                     udata.addr.buf := @src_addr;
  132.                     udata.addr.maxlen := SizeOf(src_addr);
  133.                     udata.opt.buf := nil;
  134.                     udata.opt.maxlen := 0;
  135.                     udata.udata.buf := @packet;
  136.                     udata.udata.maxlen := SizeOf(packet);
  137.                     err := OTRcvUData( ep, @udata, flags );
  138.                     if err <> noErr then begin
  139.                         leave;
  140.                     end;
  141.                     header1_size := band(packet[0], $0F)*4;
  142.                     if (packet[header1_size+0] = 3) | (packet[header1_size+0] = 11) then begin
  143.                         header2_size := band(packet[header1_size+8],$0F)*4;
  144.                         udp := @packet[header1_size+8+header2_size];
  145.                         if udp^.local_port = udp_local_port then begin
  146.                             index := FindResultIndex( udp^.remote_port );
  147.                             if (index > 0) & (icmp_results[index].remote_ip = 0) then begin
  148.                                 Microseconds( icmp_results[index].arrival_time );
  149.                                 icmp_results[index].ttl := band( packet[8], $FF );
  150.                                 icmp_results[index].remote_ip := LongIntPtr( @packet[12] )^;
  151.                                 icmp_results[index].typecode := IntegerPtr( @packet[header1_size+0] )^;
  152.                                 icmp_results[index].udp_remote_port := udp^.remote_port;
  153.                             end;
  154.                         end;
  155.                     end;
  156.                 end;
  157.             end;
  158.             kOTProviderIsClosed, kOTProviderWillClose: begin
  159.                 if rawip_ep <> nil then begin
  160.                     err := OTCloseProvider( ep );
  161.                     rawip_ep := nil;
  162.                 end;
  163.             end;
  164.             otherwise
  165.                 ;
  166.         end;
  167.     end;
  168.  
  169.     procedure UDPEventHandler ( ep: EndpointRef; event: OTEventCode; result: OTResult; cookie: univ Ptr);
  170.         var
  171.             err: OSStatus;
  172.     begin
  173. {$unused(cookie, result)}
  174.         case event of
  175.             kOTProviderIsClosed, kOTProviderWillClose: begin
  176.                 if udp_ep <> nil then begin
  177.                     err := OTCloseProvider( ep );
  178.                     udp_ep := nil;
  179.                 end;
  180.             end;
  181.             otherwise
  182.                 ;
  183.         end;
  184.     end;
  185.  
  186.     function OTOpenUDP( var ep: EndpointRef; var port: InetPort ): OSStatus;
  187.         var
  188.             err, junk: OSStatus;
  189.             retsin:InetAddress;
  190.             ret:TBind;
  191.     begin
  192.         ep := OTOpenEndpoint( OTCreateConfiguration( "udp" ), 0, nil, err );
  193.         if err = noErr then begin
  194.             err:=OTInstallNotifier( ep, @UDPEventHandler, ep );
  195.             if err = noErr then begin
  196.                 MZero(@ret, sizeof(ret));
  197.                 ret.addr.maxlen := SizeOf(InetAddress);
  198.                 ret.addr.buf := @retsin;
  199.                 err := OTBind( ep, nil, @ret );
  200.                 port := retsin.fPort;
  201.             end;
  202.             if err <> noErr then begin
  203.                 junk := OTCloseProvider( ep );
  204.             end;
  205.         end;
  206.         if err <> noErr then begin
  207.             ep := nil;
  208.         end;
  209.         OTOpenUDP := noErr;
  210.     end;
  211.  
  212.     function OTOpenRawip( var ep: EndpointRef; proc: ProcPtr ): OSStatus;
  213.         var
  214.             err, junk: OSStatus;
  215.     begin
  216.         ep := OTOpenEndpoint( OTCreateConfiguration( "rawip" ), 0, nil, err );
  217.         if err = noErr then begin
  218.             if proc <> nil then begin
  219.                 err:=OTInstallNotifier( ep, proc, ep );
  220.             end;
  221.             if err = noErr then begin
  222.                 err := OTBind( ep, nil, nil );
  223.             end;
  224.             if err = noErr then begin
  225.                 err := OTSetAsynchronous( ep );
  226.             end;
  227.             if err <> noErr then begin
  228.                 junk := OTCloseProvider( ep );
  229.             end;
  230.         end;
  231.         if err <> noErr then begin
  232.             ep := nil;
  233.         end;
  234.         OTOpenRawip := noErr;
  235.     end;
  236.  
  237. {
  238.     // According to the XTI spec, IP_TTL is an INET_IP level option that
  239.     //  determines the TTL of an IP packet.  The value of this option is
  240.     //  a UInt8.  This routine simply negotiates that option on the ep
  241.     //  endpoint.
  242. }
  243.     function DoNegotiateIP_TTLOption( ep: EndpointRef; ttl: longint): OSStatus;
  244.         var
  245.             err: OSStatus;
  246.             opt: TOption;
  247.             req: TOptMgmt;
  248.             ret: TOptMgmt;
  249.     begin
  250.         opt.level := INET_IP;
  251.         opt.optName := IP_TTL;
  252.         opt.len := kOTOneByteOptionSize;
  253.         opt.status := 0;
  254.         Ptr(@opt.value)^ := ttl;
  255.  
  256.         req.opt.buf := @opt;
  257.         req.opt.len := kOTOneByteOptionSize;
  258.         req.flags := T_NEGOTIATE;
  259.  
  260.         ret.opt.buf := @opt;
  261.         req.opt.maxlen := SizeOf(opt);
  262.  
  263.         err := OTOptionManagement(ep, @req, @ret);
  264.  
  265.         if (err = noErr) & (opt.status <> T_SUCCESS) then begin
  266.             err := opt.status;
  267.         end;
  268.         
  269.         DoNegotiateIP_TTLOption := err;
  270.     end;
  271.     
  272. {
  273.         // 33434 is the default port for unix traceroute.
  274.         //  It was chosen because it's unlikely that anyone will be listening on this
  275.         //  port.  Hence any packets that make it through will generate an ICMP
  276.         //  port unreachable error.
  277.         // [PNL - except that OT starts anonymous ports at 32768..]
  278. }
  279.  
  280. {
  281.         // The act of sending (OTSndUData) is a little more complicated than it should be.
  282.         //  Basically the ICMP errors that come back from all these bogus (short TTL)
  283.         //  packets that I send, end up as datagram errors on the sending endpoint.
  284.         //  If you attempt to send with a T_UDERR sitting on the endpoint, you get
  285.         //  a kOTLookErr which must be dealt with
  286. }
  287.     function SendUDPWithTTL( dest: InetHost; index: integer; ttl: longint; data: Ptr; datalen: longint ): OSStatus;
  288.         var
  289.             err: OSStatus;
  290.             dest_addr: InetAddress;
  291.             udata: TUnitData;
  292.     begin
  293.         err := noErr;
  294.         if     udp_ep = nil then begin
  295.             err := -1;
  296.         end;
  297.         if err = noErr then begin
  298.             err := DoNegotiateIP_TTLOption( udp_ep, ttl );
  299.         end;
  300.         if err = noErr then begin
  301.             OTInitInetAddress(dest_addr, icmp_results[index].udp_remote_port, dest);        
  302.  
  303.             udata.addr.len := SizeOf(dest_addr);
  304.             udata.addr.buf := @dest_addr;
  305.             
  306.             udata.opt.len := 0;
  307.             udata.opt.buf := nil;
  308.             
  309.             udata.udata.len := datalen;
  310.             udata.udata.buf := data;
  311.  
  312.             Microseconds( icmp_results[index].sent_time );
  313.             err := OTLFSndUData( udp_ep, udata );
  314.         end;
  315.         
  316.         SendUDPWithTTL := err;
  317.     end;
  318.  
  319.     function SendTraceroutePacket( dest: InetHost; index: integer; ttl: longint ): OSStatus;
  320.         type
  321.             UDPPacket = record
  322.                     ttl: longint;
  323.                 end;
  324.         var                
  325.             packet: UDPPacket;
  326.     begin
  327.         packet.ttl := ttl;
  328.         SendTraceroutePacket := SendUDPWithTTL( dest, index, ttl, @packet, SizeOf(packet) );
  329.     end;
  330.     
  331.     procedure CloseEndpoints;
  332.         var
  333.             junk: OSErr;
  334.             tmp: EndpointRef;
  335.     begin
  336.         if udp_ep <> nil then begin
  337.             tmp := udp_ep;
  338.             udp_ep := nil;
  339.             junk := OTCloseProvider( tmp );
  340.         end;
  341.         if rawip_ep <> nil then begin
  342.             tmp := rawip_ep;
  343.             rawip_ep := nil;
  344.             junk := OTCloseProvider( tmp );
  345.         end;
  346.     end;
  347.     
  348.     function StartTraceroute: OSStatus;
  349.         var
  350.             err: OSStatus;
  351.     begin
  352.         if (traceroutes_in_progress > 0) & (udp_ep <> nil) & (rawip_ep <> nil) then begin
  353.             err := noErr;
  354.         end else begin
  355.             err := OpenTransportSystem;
  356.             if (err = noErr) & (udp_ep = nil) then begin
  357.                 err := OTOpenUDP( udp_ep, udp_local_port);
  358.             end;
  359.             if (err = noErr) & (rawip_ep = nil) then begin
  360.                 err := OTOpenRawip( rawip_ep, @RawIPEventHandler );
  361.             end;
  362.         end;
  363.         if err = noErr then begin
  364.             Inc(traceroutes_in_progress);
  365.         end else begin
  366.             CloseEndpoints;
  367.         end;
  368.         StartTraceroute := err;
  369.     end;
  370.     
  371.     procedure StopTraceroute;
  372.     begin
  373.         Dec(traceroutes_in_progress);
  374.         if traceroutes_in_progress = 0 then begin
  375.             CloseEndpoints;
  376.         end;
  377.     end;
  378.     
  379.     function InitMyTraceroute( var msg: integer ): OSStatus;
  380.     begin
  381. {$unused(msg)}
  382.         udp_ep := nil;
  383.         rawip_ep := nil;
  384.         traceroutes_in_progress := 0;
  385.         next_udp_remote_port := min_remote_port;
  386.         InitResults;
  387.         InitMyTraceroute := noErr;
  388.     end;
  389.  
  390.     procedure FinishMytraceroute;
  391.     begin
  392.         CloseEndpoints;
  393.     end;
  394.     
  395.     procedure StartupMyTraceroute;
  396.     begin
  397.         StartupTransport;
  398.         SetStartup( InitMyTraceroute, nil, 0, FinishMytraceroute );
  399.     end;
  400.     
  401. end.
  402.